/* shellfunctions.c */

#include "shellfunctions.h"
#include "auxlib.h"
#include "video.h"
#include "asmfunc.h"
#include "defuns.h"
#include "keyboard.h"
#include "serial.h"


static void parseUpdateClock(char * buffer);
static void validateInput(void);
static void makeClockUpdate(void);
static int IS_LEAP(int y);
static void get_P_type(int type);
static int getVendorID(char * info);
static void get_P_Family(int id, int fam, int ext_fam);

/* Variables globales para validaciones de RTC */
static int daysPerMonth[13] = {0, 31, 29, 31, 30, 31, 30, 31, 31, 
			30, 31, 30, 31};
static int RTCinput[7] = {-1, -1, -1, -1, -1, -1, -1};
static int RTCVal[7] = {0, 0, 0, 0, 0, 0, 0};
static char RTCStr[7][3];
static int RTCregs[7] = {RTC_SEC_REG, RTC_MIN_REG, RTC_HOUR_REG, 
		RTC_DAY_REG, RTC_MONTH_REG, RTC_CENT_REG, RTC_YEAR_REG};
//static int RTCregsAl[3] = {RTC_SEC_AL_REG, RTC_MIN_AL_REG, RTC_HOUR_AL_REG};


/* Prompt del shell */
static char prompt[] = ">> ";

/* Lista de comandos */
static char keyBoard_cmd[] = "setkbd ";
static char clear_cmd[] = "clears";
static char clock_cmd[] = "clock ";
static char help_cmd[] = "help";
static char example2_cmd[] = "example clock1";
static char cpu_id[] = "cpu";
static char chat_cmd[] = "chat";

/*Posibles tipos de teclado*/
static char latinKeyboard[] = "la";
static char usKeyboard[] = "us";


static char help_str[] = "\n\nAYUDA\n\n"
	"clears     Limpia la pantalla\n"
	"clock      Permite cambiar la fecha y hora del sistema\n"
	"cpu        Muestra informacion del procesador\n"
	"chat       Permite enviar mensajes por el puerto serie.\n"
	"help       Muestra los comandos del shell\n"
	"setkbd     Permite cambiar la configuracion regional del teclado\n\n"
	"atajos:     \n\n"
	"	    ctrl+c: cpu\n\n"
	"	    ctrl+r: clear\n\n"
	"	    ctrl+h: help\n\n"
	"ejemplos:  'example clock1' \n";



static char Intel_Family[][30] = {"80486", "Intel P5", "Intel P6, P2, P3, PM, Core 2", "Intel Itanium (IA-64)"};
static char Intel_Ext_Family[][30] = {"Intel P4", "Intel Itanium 2 (IA-64)", "Intel Itanium 2 DC (IA-64)"};

static char AMD_Family[][30] = {"AMD 5x86", "AMD K5, K6", "AMD K7"};
static char AMD_Ext_Family[][30] = {"AMD K8", "AMD K8L"};

static char Cyrix_Family[][30] = {"Cyrix 5x86", "Cyrix M1", "Cyrix M2"};

static char NexGen_Family[][30] = {"NexGen Nx586"};

static char Centaur_Family[][30] = {"Centaur C6, C2, C3"};

static char Rise_Family[][30] = {"Rise mP6"};

static char SiS_Family[][30] = {"SiS 55x"};

static char Transmeta_Family[][30] = {"Transmeta Crusoe"};

static char Transmeta_Ext_Family[][30] = {"Transmeta Efficeon"};

static char Geode_Family[][30] = {"Geode"};



/* Ya está separado por Familia, Modelo, Familia Extendida y Modelo Extendido
/ Faltaría hacer unas comparaciones y que imprima en pantalla el modelo y demás
/ Preguntar que hacer cuando hay MUCHOS modelos con el mismo núnmero.
*/

static void
get_P_type(int type)
{
	static char pro_type1[] = "Primary Processor";
	static char pro_type2[] = "Overdrive Processor";
	static char pro_type3[] = "Secondary Processor";
	static char pro_type4[] = "Reserved";

	if(type==0x0000)
		writeCursor(pro_type1);
	else if(type==0x1000)
		writeCursor(pro_type2);
	else if(type==0x2000)
		writeCursor(pro_type3);
	else writeCursor(pro_type4);

	return ;
}

static int
getVendorID(char * info)
{
	static char ids[][13] = {"GenuineIntel", "AuthenticAMD", "CyrixInstead", "NexGenDriven", "CentaurHauls", "RiseRiseRise", "SiS SiS SiS", "GenuineTMx86", "Geode by NSC"};
	int i;
	
	for(i=0; i<CANT_IDS; i++)
	{
		if(strncmp(info, ids[i], strlen(ids[i]))==0)
			return i;
	}
	
	return -1;			
}

static void
get_P_Family(int id, int fam, int ext_fam)
{
	if(id==0)
	{
		if(fam==0x400)
			writeCursor((char *)Intel_Family[0]);
		else if(fam==0x500)
			writeCursor((char *)Intel_Family[1]);
		else if(fam==0x600)
			writeCursor((char *)Intel_Family[2]);
		else if(fam==0x700)
			writeCursor((char *)Intel_Family[3]);
		else if(fam==0x000 || fam==0xF00)
		{
			if(ext_fam==0x0)
				writeCursor((char *)Intel_Ext_Family[0]);
			else if(ext_fam==0x100000)
				writeCursor((char *)Intel_Ext_Family[1]);
			else if(ext_fam==0x200000)
				writeCursor((char *)Intel_Ext_Family[2]);
		}

	}else if(id==1)
	{
		if(fam==0x400)
			writeCursor((char *)AMD_Family[0]);
		else if(fam==0x500)
			writeCursor((char *)AMD_Family[1]);
		else if(fam==0x600)
			writeCursor((char *)AMD_Family[2]);
		else if(fam==0xF00)
		{
			if(ext_fam==0x0)
				writeCursor((char *)AMD_Ext_Family[0]);
			else if(ext_fam==0x100000)
				writeCursor((char *)AMD_Ext_Family[1]);
		}
	}else if(id==2)
	{
		if(fam==0x400)
			writeCursor((char *)Cyrix_Family[0]);
		else if(fam==0x500)
			writeCursor((char *)Cyrix_Family[1]);
		else if(fam==0x600)
			writeCursor((char *)Cyrix_Family[2]);
	}else if(id==3)
	{
		if(fam==0x400)
			writeCursor((char *)NexGen_Family[0]);
	}else if(id==4)
	{
		if(fam==0x500)
			writeCursor((char *)Centaur_Family[0]);
	}else if(id==5)
	{
		if(fam==0x500)
			writeCursor((char *)Rise_Family[0]);
	}else if(id==6)
	{
		if(fam==0x500)
			writeCursor((char *)SiS_Family[0]);
	}else if(id==7)
	{
		if(fam==0x500)
			writeCursor((char *)Transmeta_Family[0]);
		else if(fam==0xF00)
		{
			if(ext_fam==0x0)
				writeCursor((char *)Transmeta_Ext_Family[0]);
		}
	}else if(id==8)
	{
		if(fam==0x500)
			writeCursor((char *)Geode_Family[0]);
	}
}

void
cpu_type(int id)
{
	static char tag_type[] = "TYPE: ";
	static char tag_fam[] ="FAMILY: ";
	int num=0;
	int family=0xf00, ext_family=0xff00000, type=0x3000;
	
	num=cputype();
	
	type = type & num;
	family = family & num;
	ext_family = ext_family & num;

	nextLine();
	writeCursor(tag_type);
	get_P_type(type);

	nextLine();
	writeCursor(tag_fam);
	get_P_Family(id, family, ext_family);

	return;
}

void
cpu(void)
{
	static char tag[] = "CPUID: ";
	char info[14];
	int id;

	
	info[12]='\0';


	nextLine();
	cpustring((char *)info);


	writeCursor(tag);
	writeCursor(info);
	
	if((id = getVendorID((char *)info)) != -1)
		cpu_type(id);

}

void
chat(char * buffer)
{
	buffer += strlen(chat_cmd);

	if(*(buffer++)=='\0')
		return;

	serial_write(PORT_COM1, START_MSJ);
	while(*buffer!='\0')
	{
		serial_write (PORT_COM1, *buffer++);
	}
	serial_write(PORT_COM1, END_MSJ);

}

/*Actualiza el reloj en memoria seg�n el reloj verdadero*/
int
saveRTC(void)
{
	int i, ans;
		
	for (i = 0; i < 7; i++)
	{
		readclock(RTCregs[i], (int) &ans);
		RTCVal[i]=ans;
		itoa(ans, RTCStr[i]);
	}


	return 0;
}


void
incrementClock(void)
{

	if(RTCVal[0]==59)
	{
		/*Si paso 1 hora levanto el clock real*/
		if(RTCVal[1]==59)
		{
			saveRTC();
			
			return;
		}else
		{
			RTCVal[1]++;
		}
		itoa(RTCVal[1], RTCStr[1]);
		RTCVal[0]=0;
	}else
	{
		RTCVal[0]++;
	}
	itoa(RTCVal[0], RTCStr[0]);

	
}

/* Imprime la fecha y hora en pantalla cada 18 interrupciones del timer-tick */
void
printRCT(void)
{
	int cursor, i;

		
	/* imprime el dia y el mes */
	for (cursor = 69, i = 3; i <= 4; i++)
	{
		if (RTCVal[i] < 10)
		{
			putChar('0', cursor++, 0);
			write(RTCStr[i], cursor++, 0);
		}
		else
		{
			write(RTCStr[i], cursor, 0);
			cursor += 2;
		}
		putChar('/', cursor++, 0);
	}

	/* imprime los primeros dos digitos del anio */
	write(RTCStr[5], cursor, 0);
	cursor += 2;
	/* imprime los ultimos dos digitos del anio */
	if (RTCVal[6] < 10)
	{
		putChar('0', cursor++, 0);
		write(RTCStr[6], cursor++, 0);
	}
	else
	{
		write(RTCStr[6], cursor, 0);
		cursor += 2;
	}

	/* imprime la hora, minutos y segundos */
	for (cursor = 70, i = 2; i >= 0; i--)
	{
		if (RTCVal[i] < 10)
		{	putChar('0', cursor++, 1);
			write(RTCStr[i], cursor++, 1);
		}
		else 
		{
			write(RTCStr[i], cursor, 1);
			cursor += 2;
		}

		if (i != 0)
		{
			putChar(':', cursor++, 1);
		}
	}	



}



/* Cambia la fecha y hora del RTC segun lo ingresado por stdin */
int
updateClock(char * buffer)
{		
	parseUpdateClock(buffer);
	saveRTC();
	return 0;
}


static void
parseUpdateClock(char * buffer)
{
	int i;

	i = 0;

	while(buffer[i] != '\0')
	{
		if (buffer[i] != '-')
			i++;
		else
		{
			i++;
			if (buffer[i] == 'h')
			{
				i++;
				RTCinput[2] = atoi(buffer + i);
			}
			else if (buffer[i] == 'm')
			{
				i++;
				RTCinput[1] = atoi(buffer + i);
			}
			else if (buffer[i] == 's')
			{
				i++;
				RTCinput[0] = atoi(buffer + i);
			}
			else if (buffer[i] == 'd')
			{
				i++;
				RTCinput[3] = atoi(buffer + i);
			}
			else if (buffer[i] == 'n')
			{	
				i++;
				RTCinput[4] = atoi(buffer + i);
			}
			else if (buffer[i] == 'y')
			{
				i++;
				RTCinput[6] = atoi(buffer + i);
				RTCinput[5] = RTCinput[6]/100;
				RTCinput[6] %= 100;
			}
			
			i++;
		}		
	}

	for (i = 0; i < 7; i++)
		if (RTCinput[i] == -1)
			readclock(RTCregs[i], (int) (RTCinput + i));

	validateInput();
	makeClockUpdate();
	for (i = 0; i < 7; i++)
		RTCinput[i] = -1;

	return;
}

static void
validateInput(void)
{
	if (RTCinput[2] < 0 || RTCinput[2] > 23)	/*valida horas*/
		RTCinput[2] = -1;

	if (RTCinput[1] < 0 || RTCinput[1] > 59)	/* valida minutos */
		RTCinput[1] = -1;

	if (RTCinput[0] < 0 || RTCinput[0] > 59)	/* valida segundos */
		RTCinput[0] = -1;

	if (RTCinput[3] < 1 || RTCinput[3] > 31)	/* valida dia */
		RTCinput[3] = -1;

	if (RTCinput[4] < 1 || RTCinput[4] > 12)	/* valida mes */
		RTCinput[4] = -1;

	if (RTCinput[5] * 100 + RTCinput[6] < 1901 || 
		RTCinput[5] * 100 + RTCinput[6] > 2037) /* valida anio */
	{
		RTCinput[5] = -1;
		RTCinput[6] = -1;
	}

	/* valida dias en el mes */
	if (RTCinput[4] != -1 && RTCinput[3] > daysPerMonth[RTCinput[4]])
		RTCinput[3] = -1;

	/* valida anio biciesto */
	if (RTCinput[3] == 29 && RTCinput[4] == 2 && !IS_LEAP(RTCinput[5] * 100 + RTCinput[6]))
	{
		RTCinput[3] = -1;
		RTCinput[4] = -1;
		RTCinput[5] = -1;
		RTCinput[6] = -1;
	}

	return;
}




static void
makeClockUpdate(void)
{
	int i;

	for (i = 0; i < 7; i++)
		if (RTCinput[i] != -1)
			writeclock(RTCregs[i], RTCinput[i]);
	
	return;
}


static int
IS_LEAP(int y)
{
	return (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0);
}

void
printPrompt(void)
{
	putCharCursorAllScreen('\n');
	writeCursorAllScreen(prompt);
}

void
help(void)
{
	writeCursor(help_str);
}

int
processCommand(char * cmd)
{
	int prossesLengh;
	
	if(!strcmp(cmd, clear_cmd))
	{
		clearBash();
		return TRUE;
	}
	else if (!strncmp(cmd, clock_cmd, strlen(clock_cmd)-1))
	{
		updateClock(cmd);
		return TRUE;
	}
	else if (!strcmp(cmd, help_cmd))
	{
		help();
		return TRUE;
	}
	else if(!strncmp(cmd, keyBoard_cmd, strlen(keyBoard_cmd)-1))
	{
		prossesLengh = strlen(keyBoard_cmd);
		changeKeyBoard(&(cmd[prossesLengh]));
		return TRUE;
	}
	else if (!strcmp(cmd, example2_cmd))
	{
		exampleClock1();
		return TRUE;
	}
	else if(cmd[0]=='\0' || isSpace(cmd[0]) )
		return FALSE;
	else if (!strncmp(cmd, chat_cmd, strlen(chat_cmd)-1))
	{
		chat(cmd);
		return TRUE;
	}
	else if (!strcmp(cmd, cpu_id))
	{
		cpu();
		return TRUE;
	}
	else
	{
		noCommandRecognized();
		return FALSE;
	}
}

void clearBash(void)
{
	clearScreen();
	freeKeyBuffer();
}


void
noCommandRecognized()
{
	static char noCmd[] = "Comando no valido";

	nextLine();
	writeCursor(noCmd);

	
	return;
}

void
commandMalformed()
{
	static char noCmd[] = "Comando mal formado";

	nextLine();
	writeCursor(noCmd);

	
	return;
}


void
exampleClock1(void)
{
	static char cmd[] = "clock -y 2002 -n 2 -d 12 -h 10 -m 15 -s 12";
	int i=0;

	printPrompt();
	while (cmd[i])
		addToKeyBuffer(cmd[i++]);

	writeCursor(cmd);
	updateClock(cmd);
	

}


void
changeKeyBoard(char* kb)
{
	if(!strcmp(kb, latinKeyboard))
		setKeyBoard(LATIN_KEYBOARD);
	else if(!strcmp(kb, usKeyboard))
		setKeyBoard(US_KEYBOARD);
	else
		commandMalformed();
}



